package com.qk.management.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qk.code.BusinessEnum;
import com.qk.code.ParamEnum;
import com.qk.constant.BusinessConstant;
import com.qk.constant.ClueConstant;
import com.qk.dto.clue.ClueDTO;
import com.qk.dto.clue.ClueQueryDTO;
import com.qk.dto.clue.MarkFalseClueDTO;
import com.qk.entity.Business;
import com.qk.entity.Clue;
import com.qk.entity.ClueTrackRecord;
import com.qk.entity.User;
import com.qk.exception.BusinessException;
import com.qk.management.annotation.ClueStateMachineTransition;
import com.qk.management.annotation.LogAnno;
import com.qk.management.config.UserThreadLocal;
import com.qk.management.mapper.BusinessMapper;
import com.qk.management.mapper.ClueMapper;
import com.qk.management.mapper.ClueTrackRecordMapper;
import com.qk.management.mapper.UserMapper;
import com.qk.management.service.ClueService;
import com.qk.management.statemachine.event.ClueEvent;
import com.qk.result.PageResult;
import com.qk.vo.clue.CluePoolQueryVO;
import com.qk.vo.clue.ClueQueryVO;
import com.qk.vo.clue.ClueTrackRecordVO;
import com.qk.vo.clue.ClueVO;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author:zhengbowen
 * @Date: 2025/10/11 19:40
 * @Description: 线索服务实现类
 */
@Service
@RequiredArgsConstructor
public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements ClueService {

    private final ClueMapper clueMapper;
    private final UserMapper userMapper;
    private final ClueTrackRecordMapper clueTrackRecordMapper;
    private final BusinessMapper businessMapper;

    @Override
    @LogAnno
    public void insert(Clue clue) {
        clue.setCreateTime(LocalDateTime.now());
        clue.setUpdateTime(LocalDateTime.now());
        clue.setStatus(ClueConstant.CLUE_STATUS_WAITING);
        this.baseMapper.insert(clue);
    }

    @Override
    @LogAnno
    @Transactional(rollbackFor = Exception.class)
    @ClueStateMachineTransition(event = ClueEvent.ASSIGN, machineId = "#clueId", queryFromDatabase = true)
    public void updateByClueId(Integer clueId, Integer userId) {
        Clue clue = Clue.builder()
                .id(clueId)
                .userId(userId)
                .updateTime(LocalDateTime.now())
                .build();
        this.baseMapper.updateById(clue);
    }

    @Override
    public PageResult<CluePoolQueryVO> listCluesInPool(ClueQueryDTO clueQueryDTO) {
        PageHelper.startPage(clueQueryDTO.getPage(), clueQueryDTO.getPageSize());
        List<CluePoolQueryVO> clueList = this.baseMapper.listCluesInPool(clueQueryDTO);
        PageInfo<CluePoolQueryVO> pageInfo = new PageInfo<>(clueList);
        return PageResult.<CluePoolQueryVO>builder()
                .total(pageInfo.getTotal())
                .rows(pageInfo.getList())
                .build();
    }

    @Override
    @LogAnno
    @Transactional(rollbackFor = Exception.class)
    @ClueStateMachineTransition(event = ClueEvent.CONVERT_TO_BUSINESS, machineId = "#id", queryFromDatabase = true)
    public void convertToBusiness(Integer id) {
        Clue clue = this.getById(id);
        Business business = BeanUtil.copyProperties(clue, Business.class);
        business.setId(null);
        business.setUserId(null);
        business.setNextTime(null);
        business.setStatus(BusinessConstant.BUSINESS_STATUS_WAIT);
        business.setClueId(clue.getId());
        business.setCreateTime(LocalDateTime.now());
        business.setUpdateTime(LocalDateTime.now());
        businessMapper.insert(business);
    }

    @Override
    @LogAnno
    @Transactional(rollbackFor=Exception.class)
    @ClueStateMachineTransition(event = ClueEvent.MARK_FALSE, machineId = "#id", queryFromDatabase = true)
    public void falseDeleteClue(Integer id, MarkFalseClueDTO markFalseClueDTO) {
        Integer currentUserId = UserThreadLocal.getUser().getId();
        ClueTrackRecord clueTrackRecord = ClueTrackRecord.builder()
                .clueId(id)
                .userId(currentUserId)
                .type(ClueConstant.CLUE_TYPE_FALSE)
                .falseReason(markFalseClueDTO.getReason())
                .record(markFalseClueDTO.getRemark())
                .createTime(LocalDateTime.now())
                .build();
        clueTrackRecordMapper.insert(clueTrackRecord);
    }

    @Override
    @LogAnno
    @Transactional(rollbackFor=Exception.class)
    @ClueStateMachineTransition(event = ClueEvent.FOLLOW_UP, machineId = "#clueDTO.id", queryFromDatabase = true)
    public void trackClue(ClueDTO clueDTO){
        // 先执行业务逻辑
        if (clueDTO == null || clueDTO.getId() == null) {
            throw new BusinessException(ParamEnum.PARAM_ERROR);
        }
        Integer currentUserId = UserThreadLocal.getUser().getId();
        Clue clue = new Clue();
        BeanUtils.copyProperties(clueDTO, clue);
        this.updateById(clue);
        ClueTrackRecord trackRecord = ClueTrackRecord.builder()
                .clueId(clue.getId())
                .userId(currentUserId)
                .subject(clue.getSubject())
                .level(clue.getLevel())
                .record(clueDTO.getRecord())
                .nextTime(clue.getNextTime())
                .type(ClueConstant.CLUE_TYPE_NORMAL)
                .createTime(LocalDateTime.now())
                .build();
        clueTrackRecordMapper.insert(trackRecord);
    }

    @Override
    public ClueVO getClueById(Integer id) {
        Clue clue = clueMapper.selectById(id);
        if (!ObjectUtil.isNotNull(clue)) {
            throw new BusinessException(BusinessEnum.CLUE_NOT_FOUND);
        }
        List<ClueTrackRecord> trackRecordList = clueTrackRecordMapper.selectList(
                Wrappers.<ClueTrackRecord>lambdaQuery()
                        .eq(ClueTrackRecord::getClueId, id)
                        .orderByDesc(ClueTrackRecord::getCreateTime)
        );
        Set<Integer> userIds = trackRecordList.stream()
                .map(ClueTrackRecord::getUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Integer, User> userMap;
        if (!userIds.isEmpty()) {
            List<User> users = userMapper.selectBatchIds(userIds);
            userMap = users.stream().collect(Collectors.toMap(User::getId, Function.identity()));
        } else {
            userMap = new HashMap<>();
        }
        ClueVO clueVO = new ClueVO();
        BeanUtils.copyProperties(clue, clueVO);
        List<ClueTrackRecordVO> trackRecordVOs = trackRecordList.stream().map(record -> {
            ClueTrackRecordVO recordVO = new ClueTrackRecordVO();
            BeanUtils.copyProperties(record, recordVO);
            if (record.getUserId() != null && userMap.containsKey(record.getUserId())) {
                recordVO.setAssignName(userMap.get(record.getUserId()).getName());
            }
            return recordVO;
        }).collect(Collectors.toList());
        clueVO.setTrackRecords(trackRecordVOs);
        return clueVO;
    }

    @Override
    public PageResult<ClueQueryVO> listClues(ClueQueryDTO clueQueryDTO) {
        PageHelper.startPage(clueQueryDTO.getPage(), clueQueryDTO.getPageSize());
        List<ClueQueryVO> clueList = this.baseMapper.listClues(clueQueryDTO);
        PageInfo<ClueQueryVO> pageInfo = new PageInfo<>(clueList);
        return PageResult.<ClueQueryVO>builder()
                .total(pageInfo.getTotal())
                .rows(pageInfo.getList())
                .build();
    }
}
